package cn.tedu.knows.auth.security;

import cn.tedu.knows.auth.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import java.util.Arrays;

@Configuration
//这个注解表示当前类是配置授权服务器类
//和Oauth2框架的组件结合读取下面配置,实现授权功能
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    //添加依赖
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private UserDetailsServiceImpl userDetailsService;

    //配置授权服务器各种登录参数
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // Oauth2 框架实际上提供的标准在java程序中体现为
        //提供了很多控制器方法,这里的 endpoints 翻译为端点
        //就是浏览器访问授权服务器时,Oauth2 提供的控制器方法
        endpoints
                //设置可用的授权管理器对象
                .authenticationManager(authenticationManager)
                //设置登录时运行的获得用户详情的方法
                .userDetailsService(userDetailsService)
                //设置登录时允许的访问方法(值允许post方法访问)
                .allowedTokenEndpointRequestMethods(HttpMethod.POST)
                //配置生成令牌的对象
                .tokenServices(tokenService());

    }

    //添加依赖:令牌生成规则,我们自己保存到Spring容器中的
    @Resource
    private TokenStore tokenStore;
    //添加依赖: 客户端详情类,
    // 是SpringCloudSecurity结合Oauth2提供的
    @Resource
    private ClientDetailsService clientDetailsService;

    @Resource
    private JwtAccessTokenConverter accessTokenConverter;

    //配置生成令牌的方法,需要保存到Spring容器中
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        //创建TokenService实现类对象
        DefaultTokenServices services = new DefaultTokenServices();
        //设置令牌生成规则
        services.setTokenStore(tokenStore);

        //实例化令牌增强对象,使用Jwt令牌
        TokenEnhancerChain chain = new TokenEnhancerChain();
        //将jwt转换器设置到令牌增强对象中
        chain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        //设置令牌增强对象到令牌生成器中
        services.setTokenEnhancer(chain);

        //设置令牌有效期(单位是秒 3600 是一小时)
        services.setAccessTokenValiditySeconds(3600);
        //设置客户端详情
        services.setClientDetailsService(clientDetailsService);

        return services;
    }

    //获得加密对象
    @Resource
    private PasswordEncoder passwordEncoder;
    //设置当前授权服务器许可的客户端信息

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() //内存保存客户端信息
                .withClient("knows") //设置客户端名称
                .secret(passwordEncoder.encode("123456"))// 客户端登录要密码,密码要加密
                .scopes("main")//赋予客户端权限
                .authorizedGrantTypes("password");//设置允许客户端登录的类型

    }
    //如果认证成功,当前客户端可以使用Oauth2的资源设置

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //是否允许当前客户端访问生成令牌的端点
                .tokenKeyAccess("permitAll()")
                //是否允许当前客户端访问验证令牌的端点
                .checkTokenAccess("permitAll()")
                //允许通过验证的客户端获得令牌
                .allowFormAuthenticationForClients();

    }
}
